﻿using System;
using System.Linq;

namespace Flagwind.Data
{
	/// <summary>
	/// Command 条件对象，兼容老版本的Command2对象。
	/// </summary>
	public class Filter
	{
		#region 私有字段

		private Command _Command;
		private string _whereString;

		#endregion

		#region 公共属性

		public string WhereString
		{
			get
			{
				return _whereString;
			}
		}

		public Command Command
		{
			get
			{
				return _Command;
			}
		}

		#endregion

		#region 构造方法

		public Filter(Command command)
		{
			_Command = command;
			_whereString = " 1=1 ";
		}

		#endregion

		#region 公共方法

		/// <summary>
		/// 选取 与 条件
		/// </summary>
		/// <param name="field">实体对象的属性</param>
		/// <param name="@operator">SQL 比较条件，如"=","LIKE","IS" 等</param>
		/// <param name="value">要比较的值</param>
		/// <returns>多条件表达式</returns>
		public Filter And(object field, string @operator, object value)
		{
			string currFieldName = _Command.TakeOneStackFields().Field;
			return SubCondition(" AND ", currFieldName, @operator, value);
		}

		/// <summary>
		/// 选取 或 条件
		/// </summary>
		/// <param name="field">实体对象的属性</param>
		/// <param name="operator">SQL 比较条件，如"=","LIKE","IS" 等</param>
		/// <param name="value">要比较的值</param>
		/// <returns>多条件表达式</returns>
		public Filter Or(object field, string @operator, object value)
		{
			string currFieldName = _Command.TakeOneStackFields().Field;
			return SubCondition(" OR  ", currFieldName, @operator, value);
		}

		/// <summary>
		/// 选取 非 条件
		/// </summary>
		/// <param name="field">实体对象的属性</param>
		/// <param name="operator">SQL 比较条件，如"=","LIKE","IS" 等</param>
		/// <param name="value">要比较的值</param>
		/// <returns>多条件表达式</returns>
		public Filter Not(object field, string @operator, object value)
		{
			string currFieldName = _Command.TakeOneStackFields().Field;
			return SubCondition(" NOT ", currFieldName, @operator, value);
		}

		/// <summary>
		/// 选取 字段 列表条件
		/// </summary>
		/// <param name="field">实体对象的属性</param>
		/// <param name="values">值列表</param>
		/// <returns>条件表达式</returns>
		public Filter In(object field, object[] values)
		{
			_whereString += GetInCondition(field, values, "IN ");
			return this;
		}

		/// <summary>
		/// 以另外一个Command条件作为In的子查询
		/// </summary>
		/// <param name="field">属性字段</param>
		/// <param name="command">Command表达式</param>
		/// <returns></returns>
		public Filter In(object field, Command command)
		{
			return In(field, command, true);
		}

		/// <summary>
		/// 构造Not In查询条件
		/// </summary>
		/// <param name="field">字段</param>
		/// <param name="values">值数组</param>
		/// <returns></returns>
		public Filter NotIn(object field, object[] values)
		{
			_whereString += GetInCondition(field, values, " NOT IN ");
			return this;
		}

		/// <summary>
		/// 以另外一个Command条件作为Not In的子查询
		/// </summary>
		/// <param name="field">属性字段</param>
		/// <param name="command">Command表达式</param>
		/// <returns></returns>
		public Filter NotIn(object field, Command command)
		{
			return In(field, command, false);
		}

		#endregion

		#region 私有方法

		private Filter SubCondition(string logicType, string fieldName, string @operator, object value)
		{
			@operator = string.IsNullOrEmpty(@operator) ? "=" : @operator.Trim().ToLower();
			if(@operator == "=" || @operator == ">=" || @operator == ">" || @operator == "<=" || @operator == "<" || @operator == "<>" || @operator.StartsWith("like"))
			{
				var tableNameField = new TableNameField
				{
					Entity = this._Command.currEntity, Field = fieldName, FieldValue = value
				};
				_whereString += logicType + "[" + fieldName + "] " + @operator + " " + _Command.CreateParameter(tableNameField);
			}
			else if(@operator.StartsWith("is"))
			{
				string valueString = value.ToString().ToUpper();
				if(valueString == "NULL" || valueString == "NOT NULL")
				{
					_whereString += logicType + fieldName + " IS " + valueString;
				}
				else
				{
					throw new FormatException("IS 条件只能是 NULL 或者 NOT NULL");
				}
			}
			else
			{
				throw new Exception("比较符必须是 =, >, >=, <, <=, <>, LIKE, IS 中的一种。");
			}
			return this;
		}

		private string GetInCondition(object field, object[] values, string type)
		{
			var tnf = _Command.TakeOneStackFields();
			var fieldName = tnf.Field;
			var buffer = string.Empty;
			var result = string.Empty;
			if(field == null || field is string)
			{
				buffer = values.Aggregate(buffer, (current, obj) => current + ("," + _Command.CreateParameter(tnf, obj.ToString())));
			}
			else if(field is DateTime)
			{
				buffer = values.Aggregate(buffer, (current, obj) => current + ("," + _Command.CreateParameter(tnf, (DateTime)obj)));
			}
			else
			{
				foreach(object obj in values)
				{
					if(obj != null)
					{
						buffer += "," + obj.ToString();
					}
					else
					{
						buffer += ",NULL";
					}
				}
			}
			if(buffer != "")
			{
				result = " AND [" + fieldName + "] " + type + " (" + buffer.TrimStart(',') + ")";
			}
			return result;
		}

		private Filter In(object field, Command command, bool isIn)
		{
			string inString = isIn ? " IN " : " NOT IN ";
			string childSql = command.ToString().Replace("@P", "@INP").Replace("@CP", "@INCP");
			if(command.sql_fields.IndexOf(',') > 0)
			{
				throw new Exception("Command 语法错误，包含在In查询中的子查询只能使用 1 个实体属性，请修改子查询的 Select 参数。");
			}
			string currFieldName = _Command.TakeOneStackFields().Field;
			_whereString += " AND [" + currFieldName + "]" + inString + "  (\r\n" + childSql + ")";
			foreach(string key in command.Parameters.Keys)
			{
				_Command.Parameters.Add(key.Replace("@P", "@INP").Replace("@CP", "@INCP"), command.Parameters[key]);
			}
			return this;
		}

		#endregion
	}
}